package protocol

import (
	"bytes"
	"encoding/hex"
	"encoding/json"
	"errors"
	"fmt"
	"golang.org/x/text/encoding/simplifiedchinese"
	"golang.org/x/text/transform"
	"io/ioutil"
	"iot-base/auth/api/logic/platformmanager/airdata"
	"iot-base/auth/api/logic/platformmanager/platformconn"
	"iot-base/auth/api/logic/platformmanager/until"
	"iot-base/auth/logger"
	"iot-base/auth/model"
	"iot-base/auth/svc"
	"iot-base/common/params"
	"strconv"
	"strings"
	"time"
)

type countryType struct {
	head     string
	standard bool
}

type CountryProtocol struct {
	pro     []string
	airData map[string][]countryType
	airDev  map[string][]countryType
}

func NewCountryProtocol() *CountryProtocol {
	return &CountryProtocol{
		//此处map存储记录当前使用的标志
		pro: []string{"co", "no", "no2", "nox", "so2", "o3", "pm2.5", "pm10"},
		airData: map[string][]countryType{
			"5minute": {{"JZ12", true}, {"JR12", false}},
			"hour":    {{"JZ16", true}, {"JR16", false}},
		},
		airDev: map[string][]countryType{
			"5minute": {{"JC07", true}},
			"hour":    {{"JC08", true}},
		},
	}
}

func (c *CountryProtocol) RegisterData(id int64, style, mode string) {
	if c == nil {
		logger.Log.Error("Country protocol is nil")
		return
	}
	switch mode {
	case "air":
		switch style {
		case "real":
			c.registerRealData(id)
		case "5minute":
			c.register5MinuteData(id)
		case "hour":
			c.registerHourData(id)
		}
	case "dev":
		switch style {
		case "5minute":
			c.register5minuteDevData(id)
		case "hour":
			c.registerHourDevData(id)
		}
	default:
		logger.Log.Error("no this mod")
	}
}

func (c *CountryProtocol) UnRegisterData(id int64, style, mode string) {
	if c == nil {
		return
	}
	switch mode {
	case "air":
		switch style {
		case "real":
			c.unRegisterRealData(id)
		case "5minute":
			c.unRegister5MinuteData(id)
		case "hour":
			c.unRegisterHourData(id)
		}
	case "dev":
		switch style {
		case "5minute":
			c.unRegister5minuteDevData(id)
		case "hour":
			c.unRegisterHourDevData(id)
		}
	}
}

//此出按照协议解析空气数据结构
func fmtData(params map[string]interface{}) (interface{}, interface{}, error) {
	head, ok := params["head"].(string)
	//第一部分
	//标况 30 秒值：bn01
	//实况 30 秒值：JR01
	//标况 5 分钟均值：JZ12
	//实况 5 分钟均值：JR12
	//标况 1 小时均值：JZ16
	//实况 1 小时均值：JR16
	//标况 AQI 日均值：JZ18
	//实况 AQI 日均值：JR18
	//标况 API 日均值：JZ06
	//实况 API 日均值：JR06

	platformId, ok := params["platformId"].(string)
	//第二部分 子站编号

	t, ok := params["time"].(string)
	//第三部分 数据时标 例如 2012-11-08 13:23:00

	data, ok := params["data"].(string)
	//第六部分 数据部分 例如：
	//SO2,0.010,;NO,0.001,;NO2,0.051,;NOx,0.052,;CO,0.512,B;

	if !ok {
		logger.Log.Error("param is err: head:", head, "platformId:", platformId, "time:", t, "data:", data)
		return nil, nil, errors.New(fmt.Sprint("param is err: head:", head, "platformId:", platformId, "time:", t, "data:", data))
	}
	header := head
	stationId := platformId

	Len := hex.EncodeToString([]byte{0x00, byte(len(strings.TrimSpace(header + stationId + t)))})
	//第四部分 数据头本分字符数

	Str5 := `@@@`
	//第五部分 固定字符@@@

	tek := `tek`
	//第七部分 固定分隔符 tek

	cr := func() string {
		crc := byte(0x00)
		da, _ := UTF82GB2312([]byte(strings.TrimSpace(header + stationId + t + Len + Str5 + data + tek)))
		for _, v := range da {
			crc ^= v
		}
		r := hex.EncodeToString([]byte{crc})
		return r
	}()
	//第八部分 校验码部分 取第一字节与0x00异或结果与第二字节异或，以此类推。

	end := `####`
	//第九部分 固定字符####

	verify := header + stationId + t + Len + Str5
	//需要的校验部分

	send := verify + data + tek + cr + end
	//需要发送的数据
	//fmt.Println("send:", send)
	return send, verify, nil
}

func fmtNowData(head, data, platformId string) (interface{}, interface{}, error) {
	result := map[string]interface{}{
		"head":       head,
		"data":       data,
		"platformId": platformId,
		"time":       time.Now().Format("2006-01-02 15:04:05"),
	}
	fmt.Println(fmtData(result))
	return fmtData(result)
}

//@Condition用来区分标况实况，
func sendAirData(id int64, head, pro string, airData map[string]airdata.AirData, Condition bool) error {
	value := ""
	var create time.Time
	for k, v := range airData {
		create = v.CreatTime
		value += strings.ToUpper(k) + ","
		value += fmt.Sprintf("%v", until.Convert(v.Value, k, v.Unit, "ug/m3"))
		value += fmtAirDataStatus(v.Status)
		value += ";"
		if Condition {
			value = getJValue(value)
		}
	}
	if value == "" {
		return errors.New("no data")
	}
	fmt.Println("value:", value)
	platformC := platformconn.GetPlatformConnById(id)
	if platformC == nil {
		return errors.New("no this conn")
	}
	platformId, err := getPlatformIdFromId(id)
	nowDt, nowV, err := fmtNowData(head, value, platformId)
	if err != nil {
		return err
	}

	reDataParams := make(map[string]interface{})
	reDataParams["head"] = head
	reDataParams["time"] = create.Format("2006-01-02 15:04:05")
	reDataParams["unit"] = "ug/m3"
	reDataParams["pro"] = pro
	reData, _ := getReData(platformId, airData, reDataParams)
	_ = saveReData(*reData)

	send, _ := UTF82GB2312([]byte(nowDt.(string)))
	res, err := platformC.SendByte(send, 5)
	if err != nil || res == nil {
		return err
	}
	if strings.Contains(string(res), nowV.(string)) {
		_ = delReData(*reData)
	}
	fmt.Println(head+" rev data :", string(res))
	return nil
}

func (c *CountryProtocol) registerHourData(id int64) {
	airdata.SubscribeByFunc(id, "hour", func(airData map[string]airdata.AirData) error {
		errJZ := sendAirData(id, "JZ16", "hour", airData, false)
		errJR := sendAirData(id, "JR16", "hour", airData, true)
		if errJZ != nil || errJR != nil {
			return errors.New(errJZ.Error() + errJR.Error())
		}
		return nil
	})
}

func (c *CountryProtocol) unRegisterHourData(id int64) {
	airdata.DisSubscribeAirData(id, "hour")
}

func (c *CountryProtocol) register5MinuteData(id int64) {
	airdata.SubscribeByFunc(id, "5minute", func(airData map[string]airdata.AirData) error {
		errJZ := sendAirData(id, "JZ12", "5minute", airData, false)
		errJR := sendAirData(id, "JR12", "5minute", airData, true)
		if errJZ != nil || errJR != nil {
			return errors.New(errJZ.Error() + errJR.Error())
		}
		return nil
	})
}

func (c *CountryProtocol) unRegister5MinuteData(id int64) {
	airdata.DisSubscribeAirData(id, "5minute")
}

func (c *CountryProtocol) registerRealData(id int64) {
	airdata.SubscribeByFunc(id, "real", func(airData map[string]airdata.AirData) error {
		value := ""
		valueJ := ""
		for k, v := range airData {
			value += strings.ToUpper(k) + ","
			value += fmt.Sprintf("%2.f", until.Convert(v.Value, k, v.Unit, "ug/m3"))
			value += fmtAirDataStatus(v.Status)
			value += ";"
			valueJ += getJValue(value)
		}
		if value == "" {
			return errors.New("no data")
		}
		platformC := platformconn.GetPlatformConnById(id)
		if platformC == nil {
			fmt.Println("no this conn")
			return errors.New("no this conn")
		}
		platformId, err := getPlatformIdFromId(id)
		nowDt, _, err := fmtNowData("bn01", value, platformId)
		nowDtJ, _, err := fmtNowData("JR01", valueJ, platformId)
		if err != nil {
			fmt.Println("err:", err)
			return err
		}
		send1, _ := UTF82GB2312([]byte(nowDt.(string)))
		res, err := platformC.SendByte(send1, 5)
		if err != nil {
			fmt.Println("err:", err)
			return err
		}
		fmt.Println("bn01 rev data :", string(res))
		send2, _ := UTF82GB2312([]byte(nowDtJ.(string)))
		res, err = platformC.SendByte(send2, 5)
		if err != nil {
			return err
		}
		fmt.Println("JR01 rev data :", string(res))
		return nil
	})
}

func (c *CountryProtocol) unRegisterRealData(ID int64) {
	airdata.DisSubscribeAirData(ID, "real")
}

func (c *CountryProtocol) timeCompare(value *params.ValueInRedis) bool {
	if time.Now().Unix()-value.PickTime < int64(time.Second*30) {
		return true
	} else {
		logger.Log.Error("get value time out")
		return false
	}
}

func getJValue(value string) string {
	if value == "" {
		return ""
	}
	values := strings.Split(value, ";")
	value1 := values[len(values)-2]
	value2 := strings.Split(value1, ",")
	v, _ := strconv.ParseFloat(value2[1], 10)
	return value2[0] + "," + fmt.Sprintf("%v", until.ToWorkCondition(v)) + "," + value2[2] + ";"
}

func getReData(platformId string, inData map[string]airdata.AirData, params map[string]interface{}) (*model.ReData, error) {
	uintStr := params["unit"].(string)
	proPath := params["pro"].(string)
	var pickTime time.Time
	var airData = make([]model.AirReData, 13)
	for k, v := range inData {
		pickTime = v.CreatTime
		airData = append(airData, model.AirReData{
			Pro:   strings.ToLower(k),
			Value: v.Value,
			Unit:  uintStr,
			Flag:  fmtAirDataStatus(v.Status),
		})
	}
	value, err := json.Marshal(airData)
	if err != nil {
		fmt.Println("json marshal err:", err)
	}
	reData := model.ReData{
		Type:       "airData",
		Value:      value,
		ProPath:    proPath,
		PickTime:   pickTime,
		PlatformId: platformId,
	}
	fmt.Println(reData)
	return &reData, nil
}

func saveReData(reData model.ReData) error {
	return svc.Dao.TReData.Add(reData)
}

func delReData(reData model.ReData) error {
	return svc.Dao.TReData.Del(reData)
}

func getDevReData(platformId string, inData map[string][]airdata.AirDevStatus, params map[string]interface{}) (*model.ReData, error) {
	uintStr, _ := params["unit"].(string)
	proPath := params["pro"].(string)
	var pickTime time.Time
	var airData = make([]model.AirReData, 0)
	for k, data := range inData {
		for _, v := range data {
			pickTime = v.CreatTime
			airData = append(airData, model.AirReData{
				Pro:   strings.ToLower(k),
				Value: v.Value,
				Unit:  uintStr,
				Flag:  fmtAirDataStatus(v.Status),
			})
		}
	}
	value, err := json.Marshal(airData)
	if err != nil {
		fmt.Println("json marshal err:", err)
		return nil, err
	}
	reData := model.ReData{
		Type:       "dev",
		Value:      value,
		ProPath:    proPath,
		PickTime:   pickTime,
		PlatformId: platformId,
	}
	fmt.Println(reData)
	return &reData, nil
}

func (c *CountryProtocol) SendReData(data []model.ReData) error {
	for _, v := range data {
		platformC := platformconn.GetPlatformConnById(v.ID)
		if platformC == nil {
			return errors.New("no this conn")
		}
		var inData = map[string]interface{}{
			"platformId": v.PlatformId,
			"time":       v.PickTime.Format("2006-01-02 15:04:05"),
		}
		value := ""
		valueJ := ""
		var redata = make([]model.AirReData, 13)
		err := v.Value.Scan(&redata)
		if err != nil {
			fmt.Println("json scan err :", err)
		}
		for _, p := range redata {
			getValue(value, p.Pro, p.Value)
			valueJ += getJValue(value)
		}
		for _, a := range c.airData[v.ProPath] {
			inData["head"] = a.head
			if a.standard {
				inData["value"] = value
			} else {
				inData["value"] = valueJ
			}
			dt, nowV, err := fmtData(inData)
			res, err := platformC.SendStr(dt.(string), 20)
			if err != nil {
				return err
			}
			if strings.Contains(string(res), nowV.(string)) {
				_ = delReData(v)
			}
			if string(res) == "" {
				return errors.New("send dt err")
			}
		}
	}
	return nil
}

func (c *CountryProtocol) SendDevReData(data []model.ReData) error {
	for _, v := range data {
		platformC := platformconn.GetPlatformConnById(v.ID)
		if platformC == nil {
			return errors.New("no this conn")
		}
		var inData = map[string]interface{}{
			"platformId": v.PlatformId,
			"time":       v.PickTime.Format("2006-01-02 15:04:05"),
		}
		value := ""
		var redata = make([]model.AirReData, 0)
		err := v.Value.Scan(&redata)
		if err != nil {
			fmt.Println("json scan err :", err)
		}
		for _, p := range redata {
			getValue(value, p.Pro, p.Value)
		}
		for _, v := range c.airDev[v.ProPath] {
			inData["head"] = v.head
			dt, err := fmtDevData(inData)
			res, err := platformC.SendStr(dt.(string), 20)
			if err != nil {
				return err
			}
			if string(res) == "" {
				return errors.New("send dt err")
			}
		}
	}
	return nil
}

func getValue(value, pro string, data float64) string {
	if data != 0 {
		value += strings.ToUpper(pro) + ","
		value += fmt.Sprintf("%v", data)
	}
	return value
}

func getPlatformIdFromId(id int64) (string, error) {
	platform, err := svc.Dao.TPlatform.FindById(id)
	if platform == nil {
		err = errors.New("no this record")
		return "", err
	}
	return platform.PlatformId, err
}

func fmtDevData(params map[string]interface{}) (interface{}, error) {
	header, err := getParamsStr(params, "head")
	//第一部分
	//5 分钟仪器状态值：JC07
	//1 小时仪器状态值：JC08
	if err != nil {
		return nil, err
	}
	stationId, err := getParamsStr(params, "platformId")
	//第二部分 子站编号
	if err != nil {
		return nil, err
	}
	t, err := getParamsStr(params, "time")
	//第三部分 数据时标 例如 2012-11-08 13:23:00
	if err != nil {
		return nil, err
	}
	data, err := getParamsStr(params, "data")
	//第六部分 数据部分
	if err != nil {
		return nil, err
	}

	Len := hex.EncodeToString([]byte{0x00, byte(len(strings.TrimSpace(header + stationId + t)))})
	//第四部分  数据头部分字符数

	Str5 := `@@@`
	//第五部分 固定分隔符@@@

	tek := `tek`
	//第七部分 固定分隔符tek

	cr := func() string {
		temp := byte(0x00)
		da, _ := UTF82GB2312([]byte(strings.TrimSpace(header + stationId + t + Len + Str5 + data + tek)))
		for _, v := range da {
			temp ^= v
		}
		r := hex.EncodeToString([]byte{temp})
		return r
	}()
	//第八部分 校验码部分

	end := `####`
	//第九部分 固定结束符####

	send := header + stationId + t + Len + Str5 + data + tek + cr + end

	return send, nil
}

func (c *CountryProtocol) registerHourDevData(id int64) {
	airdata.SubscribeDevByFunc(id, "hour", func(data map[string][]airdata.AirDevStatus) error {
		return sendAirDevData(id, "JC08", "hour", data)
	})
}

func (c *CountryProtocol) unRegisterHourDevData(id int64) {
	airdata.DisSubscribeAirDevData(id, "hour")
}

func (c *CountryProtocol) register5minuteDevData(id int64) {
	if c == nil {
		logger.Log.Error("country protocol is nil-----")
	}
	airdata.SubscribeDevByFunc(id, "5minute", func(data map[string][]airdata.AirDevStatus) error {
		return sendAirDevData(id, "JC07", "5minute", data)
	})
}

func sendAirDevData(id int64, head, pro string, data map[string][]airdata.AirDevStatus) error {
	devParams := make(map[string]interface{})
	platformC := platformconn.GetPlatformConnById(id)
	if platformC == nil {
		return errors.New("no this conn")
	}
	platformId, err := getPlatformIdFromId(id)
	//获取平台ID
	if err != nil {
		return err
	}
	for k, dt := range data {
		value := "<><><>"
		var create time.Time
		for _, v := range dt {
			create = v.CreatTime
			brand := strings.Split(v.Brand, ",")
			if len(brand) < 2 {
				value += " <>"
			}
			for _, b := range brand {
				if b == "" {
					value += " <>"
				}
				value += b + "<>"
			}
			value += strings.ToUpper(k) + "<>"
			value += v.Name + "<>"
			value += fmt.Sprintf("%2.f<>%s<>%v,%v", v.Value, v.Unit, v.Max, v.Min) + "<>"
			if v.Value > v.Max || v.Value < v.Min {
				value += "Y<>"
			} else {
				value += "N<>"
			}
			value += "<><>"
		}
		devParams["head"] = "JC07"
		devParams["platformId"] = platformId
		devParams["time"] = create.Format("2006-01-02 15:04:05")
		devParams["data"] = value
		devParams["pro"] = "5minute"
		devData, err := fmtDevData(devParams)
		if err != nil {
			fmt.Println("fmt dev data err:", err)
			continue
		}
		reData, _ := getDevReData(platformId, data, devParams)
		_ = saveReData(*reData)
		if err != nil {
			fmt.Println("save reData err:", err)
		}
		result := devData.(string)
		fmt.Println("send data:", result)
		send, _ := UTF82GB2312([]byte(result))
		res, err := platformC.SendByte(send, 5)
		if err != nil || res == nil {
			fmt.Println("send err:", err)
			continue
		}
		fmt.Println("--------res:", string(res))
		_ = delReData(*reData)
	}
	return nil
}

func (c *CountryProtocol) unRegister5minuteDevData(ID int64) {
	airdata.DisSubscribeAirDevData(ID, "5minute")
}

const (
	DataNormal    int = 1
	Break         int = 2
	ZeroCheck     int = 6
	PastCheck     int = 7
	DataDeficient int = 8
)

func fmtAirDataStatus(status int) string {
	var value = ""
	switch status {
	case DataNormal:
		value += ","
	case ZeroCheck:
		value += ",PZ"
	case PastCheck:
		value += ",PS"
	case DataDeficient:
		value += ",H"
	default:
		value += ",B"
	}
	return value
}

func UTF82GB2312(s []byte) ([]byte, error) {
	reader := transform.NewReader(bytes.NewReader(s), simplifiedchinese.GBK.NewEncoder())
	d, e := ioutil.ReadAll(reader)
	if e != nil {
		return nil, e
	}
	return d, nil
}

func getParamsStr(params map[string]interface{}, key string) (string, error) {
	param, ok := params[key].(string)
	if !ok {
		logger.Log.Error("param is err:", key, ":", param)
		return "", errors.New(fmt.Sprint("param is err:", key, ":", param))
	}
	return param, nil
}
